package com.gzc.just.play.last.war.centralserver.quest;



import com.gzc.just.play.last.war.centralserver.quest.model.Quest;

import com.gzc.just.play.last.war.centralserver.quest.model.QuestProgress;

import com.gzc.just.play.last.war.centralserver.quest.repository.QuestRepository;

import com.gzc.just.play.last.war.centralserver.quest.repository.QuestProgressRepository;

import com.gzc.just.play.last.war.centralserver.repository.CentralPlayerRepository;

import com.gzc.just.play.last.war.centralserver.model.Player;

import lombok.RequiredArgsConstructor;

import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;



import java.util.*;

import java.util.concurrent.CompletableFuture;

import java.util.concurrent.ConcurrentHashMap;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;



@Slf4j

@Service("central-questManager")
@RequiredArgsConstructor

public class QuestManager {



    private final QuestRepository questRepository;

    private final QuestProgressRepository questProgressRepository;

    private final CentralPlayerRepository playerRepository;

    

    private final Map<Long, Quest> questCache = new ConcurrentHashMap<>();

    

    private final Map<Long, List<QuestProgress>> playerQuestCache = new ConcurrentHashMap<>();

    

    private final ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();

    

    @Transactional

    public void initializePlayerQuests(Long playerId) {

        try {

            List<QuestProgress> existingProgress = questProgressRepository.findByPlayerId(playerId);

            if (!existingProgress.isEmpty()) {

                log.debug("Player {} already has quest progress", playerId);

                return;

            }

            

            List<Quest> initialQuests = questRepository.findByLevelRequiredLessThanEqualOrderByLevelRequired(1);

            

            for (Quest quest : initialQuests) {

                QuestProgress progress = new QuestProgress();

                progress.setPlayerId(playerId);

                progress.setQuestId(quest.getId());

                progress.setStatus(QuestProgress.Status.ACCEPTED);

                progress.setProgress(0);

                progress.setAcceptTime(System.currentTimeMillis());

                

                questProgressRepository.save(progress);

            }

            

            log.info("Initialized {} quests for player: {}", initialQuests.size(), playerId);

            

        } catch (Exception e) {

            log.error("Error initializing quests for player: {}", playerId, e);

        }

    }

    

    public List<Quest> getPlayerQuests(Long playerId) {

        try {

            List<QuestProgress> progressList = getQuestProgress(playerId);

            

            List<Quest> quests = new ArrayList<>();

            for (QuestProgress progress : progressList) {

                Quest quest = getQuest(progress.getQuestId());

                if (quest != null) {

                    quest.setProgress(progress);

                    quests.add(quest);

                }

            }

            

            return quests;

            

        } catch (Exception e) {

            log.error("Error getting quests for player: {}", playerId, e);

            return Collections.emptyList();

        }

    }

    

    @Transactional

    public CompletableFuture<Boolean> acceptQuest(Long playerId, Long questId) {

        return CompletableFuture.supplyAsync(() -> {

            try {

                Quest quest = getQuest(questId);

                if (quest == null) {

                    log.warn("Quest not found: {}", questId);

                    return false;

                }

                

                Optional<QuestProgress> existingProgress = questProgressRepository.findByPlayerIdAndQuestId(playerId, questId);

                if (existingProgress.isPresent()) {

                    log.warn("Player {} already has quest: {}", playerId, questId);

                    return false;

                }

                

                Optional<Player> playerOptional = playerRepository.findById(playerId);

                if (playerOptional.isEmpty()) {

                    log.warn("Player not found: {}", playerId);

                    return false;

                }

                

                Player player = playerOptional.get();

                if (player.getLevel() < quest.getLevelRequired()) {

                    log.warn("Player level {} is not enough for quest {}", player.getLevel(), questId);

                    return false;

                }

                

                if (!arePrerequisitesCompleted(playerId, quest)) {

                    log.warn("Prerequisites not completed for quest: {}", questId);

                    return false;

                }

                

                QuestProgress progress = new QuestProgress();

                progress.setPlayerId(playerId);

                progress.setQuestId(questId);

                progress.setStatus(QuestProgress.Status.ACCEPTED);

                progress.setProgress(0);

                progress.setAcceptTime(System.currentTimeMillis());

                

                questProgressRepository.save(progress);

                

                playerQuestCache.remove(playerId);

                

                log.info("Player {} accepted quest: {}", playerId, questId);

                return true;

                

            } catch (Exception e) {

                log.error("Error accepting quest {} for player {}", questId, playerId, e);

                return false;

            }

        }, executorService);

    }

    

    @Transactional

    public boolean completeQuest(Long playerId, Long questId) {

        try {

            Optional<QuestProgress> progressOptional = questProgressRepository.findByPlayerIdAndQuestId(playerId, questId);

            if (progressOptional.isEmpty()) {

                log.warn("Quest progress not found: {} for player: {}", questId, playerId);

                return false;

            }

            

            QuestProgress progress = progressOptional.get();

            if (progress.getStatus() == QuestProgress.Status.COMPLETED) {

                log.warn("Quest {} already completed by player: {}", questId, playerId);

                return false;

            }

            

            Quest quest = getQuest(questId);

            if (quest == null) {

                log.warn("Quest not found: {}", questId);

                return false;

            }

            

            if (progress.getStatus() != QuestProgress.Status.FINISHED) {

                log.warn("Quest {} not finished yet for player: {}", questId, playerId);

                return false;

            }

            

            progress.setStatus(QuestProgress.Status.COMPLETED);

            progress.setCompleteTime(System.currentTimeMillis());

            questProgressRepository.save(progress);

            

            giveQuestRewards(playerId, quest);

            

            activateNextQuests(playerId, quest);

            

            playerQuestCache.remove(playerId);

            

            log.info("Player {} completed quest: {}", playerId, questId);

            return true;

            

        } catch (Exception e) {

            log.error("Error completing quest {} for player {}", questId, playerId, e);

            return false;

        }

    }

    

    @Transactional

    public CompletableFuture<Boolean> abandonQuest(Long playerId, Long questId) {

        return CompletableFuture.supplyAsync(() -> {

            try {

                Optional<QuestProgress> progressOptional = questProgressRepository.findByPlayerIdAndQuestId(playerId, questId);

                if (progressOptional.isEmpty()) {

                    log.warn("Quest progress not found: {} for player: {}", questId, playerId);

                    return false;

                }

                

                QuestProgress progress = progressOptional.get();

                

                if (progress.getStatus() == QuestProgress.Status.COMPLETED) {

                    log.warn("Cannot abandon completed quest: {} for player: {}", questId, playerId);

                    return false;

                }

                

                questProgressRepository.delete(progress);

                

                playerQuestCache.remove(playerId);

                

                log.info("Player {} abandoned quest: {}", playerId, questId);

                return true;

                

            } catch (Exception e) {

                log.error("Error abandoning quest {} for player {}", questId, playerId, e);

                return false;

            }

        }, executorService);

    }

    

    @Transactional

    public void updateQuestProgress(Long playerId, String questType, int progressValue) {

        try {

            List<QuestProgress> progressList = getQuestProgress(playerId);

            

            for (QuestProgress progress : progressList) {

                if (progress.getStatus() != QuestProgress.Status.ACCEPTED) {

                    continue;

                }

                

                Quest quest = getQuest(progress.getQuestId());

                if (quest == null) {

                    continue;

                }

                

                if (!quest.getType().equals(questType)) {

                    continue;

                }

                

                int oldProgress = progress.getProgress();

                int newProgress = Math.min(oldProgress + progressValue, quest.getTargetValue());

                

                if (newProgress != oldProgress) {

                    progress.setProgress(newProgress);

                    

                    if (newProgress >= quest.getTargetValue()) {

                        progress.setStatus(QuestProgress.Status.FINISHED);

                        progress.setFinishTime(System.currentTimeMillis());

                    }

                    

                    questProgressRepository.save(progress);

                    

                    log.debug("Updated quest progress: quest={}, player={}, progress={}/{}", 

                            progress.getQuestId(), playerId, newProgress, quest.getTargetValue());

                }

            }

            

            playerQuestCache.remove(playerId);

            

        } catch (Exception e) {

            log.error("Error updating quest progress for player: {}, type: {}, value: {}", playerId, questType, progressValue, e);

        }

    }

    

    private List<QuestProgress> getQuestProgress(Long playerId) {

        List<QuestProgress> progressList = playerQuestCache.get(playerId);

        

        if (progressList == null) {

            progressList = questProgressRepository.findByPlayerId(playerId);

            playerQuestCache.put(playerId, progressList);

        }

        

        return progressList;

    }

    

    private Quest getQuest(Long questId) {

        Quest quest = questCache.get(questId);

        

        if (quest == null) {

            Optional<Quest> questOptional = questRepository.findById(questId);

            if (questOptional.isPresent()) {

                quest = questOptional.get();

                questCache.put(questId, quest);

            }

        }

        

        return quest;

    }

    

    private boolean arePrerequisitesCompleted(Long playerId, Quest quest) {

        String prerequisites = quest.getPrerequisites();

        if (prerequisites == null || prerequisites.isEmpty()) {

            return true;

        }

        

        String[] prerequisiteIds = prerequisites.split(",");

        

        for (String idStr : prerequisiteIds) {

            try {

                Long prerequisiteId = Long.valueOf(idStr.trim());

                Optional<QuestProgress> progressOptional = 

                    questProgressRepository.findByPlayerIdAndQuestId(playerId, prerequisiteId);

                

                if (progressOptional.isEmpty() || 

                    progressOptional.get().getStatus() != QuestProgress.Status.COMPLETED) {

                    return false;

                }

            } catch (NumberFormatException e) {

                log.warn("Invalid prerequisite ID: {}", idStr);

            }

        }

        

        return true;

    }

    

    private void giveQuestRewards(Long playerId, Quest quest) {

        Optional<Player> playerOptional = playerRepository.findById(playerId);

        if (playerOptional.isEmpty()) {

            return;

        }

        

        Player player = playerOptional.get();

        

        if (quest.getGoldReward() > 0) {

            player.setGold(player.getGold() + quest.getGoldReward());

        }

        

        if (quest.getDiamondReward() > 0) {

            player.setDiamond(player.getDiamond() + quest.getDiamondReward());

        }

        

        if (quest.getExpReward() > 0) {

            addExperience(player, quest.getExpReward());

        }

        

        playerRepository.save(player);

        

        

        log.info("Gave rewards to player {} for quest {}: gold={}, diamond={}, exp={}", 

                playerId, quest.getId(), quest.getGoldReward(), quest.getDiamondReward(), quest.getExpReward());

    }

    

    private void addExperience(Player player, int experience) {

        player.setExperience(player.getExperience() + experience);

        

        int requiredExp = calculateRequiredExp(player.getLevel());

        while (player.getExperience() >= requiredExp) {

            player.setExperience(player.getExperience() - requiredExp);

            player.setLevel(player.getLevel() + 1);

            requiredExp = calculateRequiredExp(player.getLevel());

            

            log.info("Player {} leveled up to {}", player.getId(), player.getLevel());

        }

    }

    

    private int calculateRequiredExp(int level) {

        return 100 * level;

    }

    

    private void activateNextQuests(Long playerId, Quest completedQuest) {

        List<Quest> allQuests = questRepository.findAll();

        

        for (Quest quest : allQuests) {

            String prerequisites = quest.getPrerequisites();

            if (prerequisites == null || !prerequisites.contains(completedQuest.getId().toString())) {

                continue;

            }

            

            Optional<QuestProgress> existingProgress = questProgressRepository.findByPlayerIdAndQuestId(playerId, quest.getId());

            if (existingProgress.isPresent()) {

                continue;

            }

            

            if (!arePrerequisitesCompleted(playerId, quest)) {

                continue;

            }

            

            Optional<Player> playerOptional = playerRepository.findById(playerId);

            if (playerOptional.isEmpty() || playerOptional.get().getLevel() < quest.getLevelRequired()) {

                continue;

            }

            

            QuestProgress progress = new QuestProgress();

            progress.setPlayerId(playerId);

            progress.setQuestId(quest.getId());

            progress.setStatus(QuestProgress.Status.ACCEPTED);

            progress.setProgress(0);

            progress.setAcceptTime(System.currentTimeMillis());

            

            questProgressRepository.save(progress);

            

            log.info("Auto-activated quest {} for player {}", quest.getId(), playerId);

        }

    }

}

